#include <vector>

#include "uint.h"
#include "cCompressorLcLz2.h"

using namespace std;

/* PROTECTED: Decompresses from this->file or this->data
 * Returns: Decompressed data
 */
u32 CompressorLcLz2::_Decompress(u8* out, u32 size)
{
	u8 byte, byte2, command;
	u16 length, address;
	u32 pos = 0, outpos = 0;

	while(((byte = GetByte(pos++)) != 0xFF) && (outpos < size))
	{
		length = byte & 0x1F;
		command = byte >> 5;

		//DebugOut(DO_ALL, "%X: %02X", pos - 1, byte);
		if (command == 7)
		{
			//Long chunk length
			command = (byte >> 2) & 7;
			length = ((byte & 3) << 8) | this->GetByte(pos++);
			//DebugOut(DO_ALL, " %02X", byte);
		}
		
		//DebugOut(DO_ALL, " - %u, %u\n", command, length);

		switch(command)
		{
		case 0:
			//Direct copy
			for (int i=0;i<=length;i++)
			{
				out[outpos] = GetByte(pos++);
				outpos++;
				if(outpos >= size) break;
			}
			break;
		case 1:
			//Byte fill
			byte = this->GetByte(pos++);
			for (int i=0;i<=length;i++)
			{
				out[outpos] = byte;
				outpos++;
				if(outpos >= size) break;
			}
			break;
		case 2:
			//Word fill
			byte = this->GetByte(pos++);
			byte2 = this->GetByte(pos++);
			for (int i=0;i<=length;i++)
			{
				out[outpos] = (i&1) ? byte2 : byte;
				outpos++;
				if(outpos >= size) break;
			}
			break;
		case 3:
			//Increasing fill
			byte = this->GetByte(pos++);
			for (int i=0;i<=length;i++)
			{
				out[outpos] = byte++;
				outpos++;
				if(outpos >= size) break;
			}
			break;
		case 4:
			//Repeat
			address = (this->GetByte(pos) << 8) | GetByte(pos + 1);
			pos += 2;
			//try //why is this here?
			{
				for (int i=0;i<=length;i++)
				{
					if(address >= size)
					{
						DebugOut(DO_MINOR, "Compression error @%X: copy %X / %X\n",
							pos, address, size);
						throw new InvalidDataException();
					}
					out[outpos++] = out[address++];
					if(outpos >= size) break;
				}
			}
			//catch(...)
			{
				//throw new InvalidDataException();
			}
			break;
		default:
			DebugOut(DO_MINOR, "Compression error @%X: command %u\n",
				pos, command);
			throw new InvalidDataException();
		}
	}
	
	if(byte != 0xFF) DebugOut(DO_MINOR,
		"LZ2 decompress: file at 0x%06X is too big for buffer (%u bytes)\n",
		this->baseaddr, size);
	
	//DebugOut(DO_ALL, "pos=%u byte=%02X\n", outpos, byte);
	return outpos;
}

u32 CompressorLcLz2::_Compress(u8* out, u32 size)
{
	throw new UnimplementedException();
}
